From 4975aa7920b0857a54114556c9830f7d9b69abfc Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 29 Aug 2017 17:28:10 -0400 Subject: [PATCH] Add a render node for text This is just a wrapper around a PangoGlyphString + PangoFont. Basically, the arguments that are passed to pango_renderer_draw_glyphs. --- gsk/gskenums.h | 4 +- gsk/gskrendernode.h | 9 + gsk/gskrendernodeimpl.c | 245 ++++++++++++++++++++++++- gtk/inspector/gtktreemodelrendernode.c | 1 + gtk/inspector/recorder.c | 2 + 5 files changed, 259 insertions(+), 2 deletions(-) diff --git a/gsk/gskenums.h b/gsk/gskenums.h index b2869e8932..1141485a04 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -45,6 +45,7 @@ * @GSK_SHADOW_NODE: A node that draws a shadow below its child * @GSK_BLEND_NODE: A node that blends two children together * @GSK_CROSS_FADE_NODE: A node that cross-fades between two children + * @GSK_TEXT_NODE: A node containing a glyph string * * The type of a node determines what the node is rendering. * @@ -69,7 +70,8 @@ typedef enum { GSK_ROUNDED_CLIP_NODE, GSK_SHADOW_NODE, GSK_BLEND_NODE, - GSK_CROSS_FADE_NODE + GSK_CROSS_FADE_NODE, + GSK_TEXT_NODE } GskRenderNodeType; /** diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index 0bbeed6998..4c94f8d94b 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -174,6 +174,15 @@ GskRenderNode * gsk_cross_fade_node_new (GskRenderNode GskRenderNode *end, double progress); +GDK_AVAILABLE_IN_3_92 +GskRenderNode * gsk_text_node_new (PangoFont *font, + PangoGlyphString *glyphs, + const GdkRGBA *color, + int x_offset, + int y_offset, + double base_x, + double base_y); + GDK_AVAILABLE_IN_3_90 void gsk_render_node_set_scaling_filters (GskRenderNode *node, GskScalingFilter min_filter, diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 214025b491..2d04bb3a64 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -3801,6 +3801,248 @@ gsk_cross_fade_node_get_progress (GskRenderNode *node) return self->progress; } +/*** GSK_TEXT_NODE ***/ + +typedef struct _GskTextNode GskTextNode; + +struct _GskTextNode +{ + GskRenderNode render_node; + + PangoFont *font; + PangoGlyphString *glyphs; + GdkRGBA color; + int x_offset; + int y_offset; + double base_x; + double base_y; +}; + +static void +gsk_text_node_finalize (GskRenderNode *node) +{ + GskTextNode *self = (GskTextNode *) node; + + g_object_unref (self->font); + pango_glyph_string_free (self->glyphs); +} + +static gboolean +_pango_cairo_font_install (PangoFont *font, + cairo_t *cr) +{ + cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font); + + if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS)) + return FALSE; + + cairo_set_scaled_font (cr, scaled_font); + + return TRUE; +} + +#ifndef STACK_BUFFER_SIZE +#define STACK_BUFFER_SIZE (512 * sizeof (int)) +#endif + +#define STACK_ARRAY_LENGTH(T) (STACK_BUFFER_SIZE / sizeof(T)) + +static void +gsk_text_node_draw (GskRenderNode *node, + cairo_t *cr) +{ + GskTextNode *self = (GskTextNode *) node; + int i, count; + int x_position = 0; + cairo_glyph_t *cairo_glyphs; + cairo_glyph_t stack_glyphs[STACK_ARRAY_LENGTH (cairo_glyph_t)]; + + cairo_save (cr); + + cairo_translate (cr, self->x_offset, self->y_offset); + + gdk_cairo_set_source_rgba (cr, &self->color); + if (!_pango_cairo_font_install (self->font, cr)) + goto done; + + if (self->glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_glyphs)) + cairo_glyphs = g_new (cairo_glyph_t, self->glyphs->num_glyphs); + else + cairo_glyphs = stack_glyphs; + + count = 0; + for (i = 0; i < self->glyphs->num_glyphs; i++) + { + PangoGlyphInfo *gi = &self->glyphs->glyphs[i]; + + if (gi->glyph != PANGO_GLYPH_EMPTY) + { + double cx = self->base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE; + double cy = gi->geometry.y_offset == 0 ? self->base_y : self->base_y + (double)(gi->geometry.y_offset) / PANGO_SCALE; + + if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)) + { + cairo_glyphs[count].index = gi->glyph; + cairo_glyphs[count].x = cx; + cairo_glyphs[count].y = cy; + count++; + } + } + x_position += gi->geometry.width; + } + + cairo_show_glyphs (cr, cairo_glyphs, count); + + if (cairo_glyphs != stack_glyphs) + g_free (cairo_glyphs); + +done: + cairo_restore (cr); +} + +#define GSK_TEXT_NODE_VARIANT_TYPE "(sddddiidda(uiiii))" + +static GVariant * +gsk_text_node_serialize (GskRenderNode *node) +{ + GskTextNode *self = (GskTextNode *) node; + GVariant *v; + GVariantBuilder builder; + int i; + PangoFontDescription *desc; + char *s; + + desc = pango_font_describe (self->font); + s = pango_font_description_to_string (desc); + for (i = 0; i < self->glyphs->num_glyphs; i++) + { + PangoGlyphInfo *glyph = &self->glyphs->glyphs[i]; + g_variant_builder_add (&builder, "(uiiii)", + glyph->glyph, + glyph->geometry.width, + glyph->geometry.x_offset, + glyph->geometry.y_offset, + glyph->attr.is_cluster_start); + } + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uiiii)")); + v = g_variant_new (GSK_TEXT_NODE_VARIANT_TYPE, + s, + self->color.red, + self->color.green, + self->color.blue, + self->color.alpha, + self->x_offset, + self->y_offset, + self->base_x, + self->base_y, + &builder); + + g_free (s); + pango_font_description_free (desc); + + return v; +} + +static GskRenderNode * +gsk_text_node_deserialize (GVariant *variant, + GError **error) +{ + PangoFont *font; + PangoGlyphString *glyphs; + GVariantIter iter; + GskRenderNode *result; + PangoGlyphInfo glyph; + PangoFontDescription *desc; + PangoFontMap *fontmap; + PangoContext *context; + int cluster_start; + char *s; + GdkRGBA color; + int x_offset, y_offset; + double base_x, base_y; + int i; + + if (!check_variant_type (variant, GSK_TEXT_NODE_VARIANT_TYPE, error)) + return NULL; + + g_variant_get (variant, "(&sddddiidda(uiiii))", + &color.red, &color.green, &color.blue, &color.alpha, + &x_offset, &y_offset, + &base_x, &base_y, + &s, &iter); + + desc = pango_font_description_from_string (s); + fontmap = pango_cairo_font_map_get_default (); + context = pango_font_map_create_context (fontmap); + font = pango_font_map_load_font (fontmap, context, desc); + + glyphs = pango_glyph_string_new (); + pango_glyph_string_set_size (glyphs, g_variant_iter_n_children (&iter)); + i = 0; + while (g_variant_iter_next (&iter, "(uiiii)", &glyph.glyph, &glyph.geometry.width, &glyph.geometry.x_offset, &glyph.geometry.y_offset, &cluster_start)) + { + glyph.attr.is_cluster_start = cluster_start; + glyphs->glyphs[i] = glyph; + i++; + } + + result = gsk_text_node_new (font, glyphs, &color, /* FIXME: Avoid copying glyphs */ + x_offset, y_offset, + base_x, base_y); + + pango_glyph_string_free (glyphs); + pango_font_description_free (desc); + g_object_unref (context); + g_object_unref (font); + + return result; +} + +static const GskRenderNodeClass GSK_TEXT_NODE_CLASS = { + GSK_TEXT_NODE, + sizeof (GskTextNode), + "GskTextNode", + gsk_text_node_finalize, + gsk_text_node_draw, + gsk_text_node_serialize, + gsk_text_node_deserialize +}; + +GskRenderNode * +gsk_text_node_new (PangoFont *font, + PangoGlyphString *glyphs, + const GdkRGBA *color, + int x_offset, + int y_offset, + double base_x, + double base_y) +{ + GskTextNode *self; + PangoRectangle ink_rect; + + self = (GskTextNode *) gsk_render_node_new (&GSK_TEXT_NODE_CLASS, 0); + + self->font = g_object_ref (font); + self->glyphs = pango_glyph_string_copy (glyphs); + self->color = *color; + self->x_offset = x_offset; + self->y_offset = y_offset; + self->base_x = base_x; + self->base_y = base_y; + + pango_glyph_string_extents (glyphs, font, &ink_rect, NULL); + pango_extents_to_pixels (&ink_rect, NULL); + + graphene_rect_init (&self->render_node.bounds, + x_offset + base_x + ink_rect.x, + y_offset + base_y + ink_rect.y, + ink_rect.width, + ink_rect.height); + + return &self->render_node; +} + static const GskRenderNodeClass *klasses[] = { [GSK_CONTAINER_NODE] = &GSK_CONTAINER_NODE_CLASS, [GSK_CAIRO_NODE] = &GSK_CAIRO_NODE_CLASS, @@ -3818,7 +4060,8 @@ static const GskRenderNodeClass *klasses[] = { [GSK_ROUNDED_CLIP_NODE] = &GSK_ROUNDED_CLIP_NODE_CLASS, [GSK_SHADOW_NODE] = &GSK_SHADOW_NODE_CLASS, [GSK_BLEND_NODE] = &GSK_BLEND_NODE_CLASS, - [GSK_CROSS_FADE_NODE] = &GSK_CROSS_FADE_NODE_CLASS + [GSK_CROSS_FADE_NODE] = &GSK_CROSS_FADE_NODE_CLASS, + [GSK_TEXT_NODE] = &GSK_TEXT_NODE_CLASS }; GskRenderNode * diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c index 63e3001a28..1697aaf2c1 100644 --- a/gtk/inspector/gtktreemodelrendernode.c +++ b/gtk/inspector/gtktreemodelrendernode.c @@ -523,6 +523,7 @@ append_node (GtkTreeModelRenderNode *nodemodel, break; case GSK_CAIRO_NODE: + case GSK_TEXT_NODE: case GSK_TEXTURE_NODE: case GSK_COLOR_NODE: case GSK_LINEAR_GRADIENT_NODE: diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 587c72e70c..e422b9ebf3 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -181,6 +181,8 @@ node_type_name (GskRenderNodeType type) return "Blend"; case GSK_CROSS_FADE_NODE: return "CrossFade"; + case GSK_TEXT_NODE: + return "Text"; } } -- 2.30.2